package newcrunch.typography;

import java.io.InputStream;
import java.util.*;
import jsyntaxpane.util.JarServiceProvider;

/**
 * Static class to hold all the standards that are tested for Java programs,
 * and also to perform the required calculations in deriving the standards.
 * @author WL
 */
public class JavaStandards 
{
    private static String javaPropsFile = "newcrunch/typography/Java.properties";
    private static Properties javaProp;
    private static String commentPropsFile = "newcrunch/typography/JavaComments.properties";
    private static Properties commentsProp;  
    private static String[] comments;
    private static double[] cutoffs;
    
    //Property string keys to load from property file
    public static String[] props = { 
        "-",
        "SpacesAfterKeywords",
        "SpacesAfterSuperDefaultThis",
        "SpacesArdBinOps",
        "SpacesArdDot",
        "SpacesArdColonInAssertTernaryFor",
        "SpacesBeforeColonInCaseAndLabel",
        "SpacesBeforeComma",
        "SpacesAfterComma",
        "SpacesBeforeSemi",
        "SpacesAfterSemiInFor",
        "SpacesBtwMethodAndOpenParen",
        "SpacesBtwUOpAndOperand",
        "SpacesBtwCastAndExp",
        "SpacesAfterOpenParen",
        "SpacesBeforeCloseParen",
        "SpacesAfterOpenSqBracket",
        "SpacesBeforeCloseSqBracket",
        "SpacesAfterOpenAngularBracket",
        "SpacesBeforeCloseAngularBracket",
        "-",
        "-",
        "BlanksAfterPackageDecl",
        "BlanksAfterImportDecl",
        "BlanksBtwClasses",
        "BlanksBtwMethods",
        "BlanksAfterLastFieldDecl",
        "PercentageBlanks",
        "PercentageComments",
        "-",
        "-",
        "SingleChars",
        "VariableNaming",
        "ConstantNaming",
        "MethodNaming",
        "ClassNaming",
        "-",
        "-",
        "StmtsPerLine",
        "ConstructorCount",
        "MethodLen",
        "ClassLen",
        "LineOfCodeLen",
        "BracingStyle",            
    };
    
    public enum NamingStyle 
    {
        UpperCamelCase,
        lowerCamelCase,
        UPPERCASE,
        Unknown
    };    
    
    static {
        InputStream stream = JarServiceProvider.findResource(javaPropsFile);
        javaProp = new Properties();
        
        InputStream cStream = JarServiceProvider.findResource(commentPropsFile);
        commentsProp = new Properties();
        try {
            javaProp.load(stream);
            
            commentsProp.load(cStream);
            
            //load the keys into an array
            Set<Object> keys = commentsProp.keySet();
            comments = new String[keys.size()];
            keys.toArray(comments);
            
            //sort the keys according to their values, from greatest to smallest
            Arrays.sort(comments, new Comparator<String>() {
                @Override
                public int compare(String s1, String s2)
                {
                    return Double.compare(Double.parseDouble(commentsProp.getProperty(s2)),
                            Double.parseDouble(commentsProp.getProperty(s1)));
                }
            });
            
            //compile the corresponding value array
            cutoffs = new double[comments.length];
            
            for (int i = 0; i < comments.length; i++)
                cutoffs[i] = Double.parseDouble(commentsProp.getProperty(comments[i]));
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static int length()
    {
        return props.length;
    }
    
    public static String getProperty(int property)
    {
        return getProperty(props[property]);
    }
    
    public static String getProperty(String property)
    {
        return javaProp.getProperty(property);
    }
    
    /**
     * Get the list of possible comments
     * @return 
     */
    public static String[] getComments()
    {
        return reverse(comments);
    }
    
    private static String[] reverse(String[] arr)
    {
        String[] res = new String[arr.length];
        if (arr.length == 0)
            return res;
        for (int i = 0; i < arr.length/2; i++)
        {
            res[i] = arr[arr.length - i - 1];
            res[arr.length - i - 1] =  arr[i];
        }
        res[arr.length/2] = arr[arr.length/2];
        return res;
    }
    
    /**
     * Gets the best comment given to
     * the best standard.
     * @return 
     */
    public static String getBestComment()
    {
        return comments[comments.length - 2];
    }
    
    /**
     * Get the standard given a set of scores.
     * Returns the standard achieved by calculating the standard
     * deviation with that set of scores.
     * @param scores
     * @return 
     */
    public static String getStandardUsingSD(List<Integer> scores)
    {
        return getStandard(getStandardDeviation(scores));
    }
    
    /**
     * Returns the standard deviation given a set of scores.
     * @param scores
     * @return 
     */
    public static double getStandardDeviation(List<Integer> scores)
    {
        if (scores.isEmpty())
            return 0;
        
        double result = 0;
        int n = scores.size();
        
        double sum = 0;
        double squareSum = 0;
        
        for (int i = 0; i < n; i++)
        {
            sum += scores.get(i);
            squareSum += scores.get(i) * scores.get(i);
        }
        
        result = Math.sqrt((squareSum / n) - ((sum / n) * (sum / n)));
        
        return result;
    }
    
    /**
     * Gets the comment for a given standard deviation.
     * @param deviation
     * @return 
     */
    public static String getStandard(double deviation)
    {
        for (int i = comments.length - 1; i >= 0; i--)
            if (deviation <= cutoffs[i])
                return comments[i];
        
        return comments[0];
    }
    
    /**
     * Compares the identifier name with the given standard and give the 
     * appropriate comments.
     * @param result
     * @param standard
     * @return 
     */
    public static String getIdentifierNamingComment(String result, String standard)
    {
        return result.equals(standard) && !result.equals("-") ? getBestComment() : "-";
    }
    
    /**
     * Gives comments according to how many single character identifier
     * there are in the code.
     * @param count
     * @return 
     */
    public static String getSingleCharacterIdComment(int count, int standard)
    {
        if (count == 0)
            return getBestComment();
        else
            return "-";
    }
    
    public static boolean isUpperCamelCase(String text)
    {
        if (text == null || text.length() == 0)
            return true;
        
        return Character.isUpperCase(text.charAt(0)) &&
                text.indexOf("_") == -1;
    }
    
    public static boolean isLowerCamelCase(String text)
    {
        if (text == null || text.length() == 0)
            return true;
        
        return Character.isLowerCase(text.charAt(0)) &&
                text.indexOf("_") == -1;
    }
    
    public static boolean isConstant(String text)
    {
        if (text == null || text.length() == 0)
            return true;
        
        return text.toUpperCase().equals(text);
    }
    
    public static NamingStyle getNamingStyle(String text)
    {
        if (isConstant(text))
            return NamingStyle.UPPERCASE;
        
        if (isUpperCamelCase(text))
            return NamingStyle.UpperCamelCase;
        
        else if (isLowerCamelCase(text))
            return NamingStyle.lowerCamelCase;
        
        else
            return NamingStyle.Unknown;
    }
}
